home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 July: Mac OS SDK / Dev.CD Jul 00 SDK2.toast / Development Kits / Hardware / Mac OS USB DDK / Mac OS USB DDK 1.4.1 / Examples / HIDReader / HIDReader.c next >
Encoding:
C/C++ Source or Header  |  2000-04-25  |  21.4 KB  |  592 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        HIDReader.c
  3.  
  4.     Contains:    HID Library Example
  5.  
  6.     Version:    1.1 for use with USB DDK 1.4.1
  7.  
  8.  
  9.     Copyright:    © 1999-2000 by Apple Computer, Inc., all rights reserved.
  10.  
  11.  
  12.         To get current values and set them, this code requires USB 1.4, which
  13.     is newer than Mac OS 9. The example is written with enough checking
  14.     built in that it can safely execute on earlier USB versions.
  15.  
  16.         This example addresses a number of issues involved with using the HID
  17.     Library to interact with Human Input USB Devices. I have tried to group
  18.     the code for each issue in a single module separated by comments. For
  19.     this reason, not all sections may be necessary for your use. Even within
  20.     a section there may be more code than you need to use. Error checking
  21.     and memory allocation is rather simplistic to easily survive the affects
  22.     of removing unnecessary code.
  23.     
  24.         This example code is normally built within HIDReader.mcp, a Metrowerks 
  25.     5.3 Pro project. Universal Interfaces 3.3 have been downloaded from Apple's
  26.     Developer Support and installed within Code Warrior's MacOS Support folder
  27.     in order to have the correct version of HID.h.
  28. */
  29.  
  30. #define CALL_NOT_IN_CARBON 1
  31.  
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. #include <errors.h>
  35. #include <USB.h>
  36. #include <HID.h>
  37.  
  38.     //    How do we find out what vendor ID and product ID we are looking for?
  39.     // Use USB Prober to check under the Device Descriptor. This version 
  40.     // is looking for a USB device that most developers will have available,
  41.     // the Apple USB mouse.
  42.  
  43. /*
  44. Composite device (M4848)
  45.     Device VendorID/ProductID:. 0x05ac/0x0301 (Apple Computer, Inc.)
  46.  
  47. HID Descriptor:
  48. 05 01        Usage Page (Generic Desktop)
  49. 09 02        Usage (Mouse)
  50. A1 01        Collection (Application)
  51. 05 09          Usage Page (Button)
  52. 19 01          Usage Minimum...... (1) 
  53. 29 01          Usage Maximum...... (1) 
  54. 15 00          Logical Minimum.... (0) 
  55. 25 01          Logical Maximum.... (1) 
  56. 95 01          Report Count....... (1) 
  57. 75 01          Report Size........ (1) 
  58. 81 02          Input (Data)
  59. 95 01          Report Count....... (1) 
  60. 75 07          Report Size........ (7) 
  61. 81 03          Input (Constant)
  62. 05 01          Usage Page (Generic Desktop)
  63. 09 01          Usage (Pointer)
  64. A1 00          Collection (Physical)
  65. 09 30            Usage (X)
  66. 09 31            Usage (Y)
  67. 15 81            Logical Minimum.... (-127) 
  68. 25 7F            Logical Maximum.... (127) 
  69. 75 08            Report Size........ (8) 
  70. 95 02            Report Count....... (2) 
  71. 81 06            Input (Data)
  72. C0              End Collection 
  73. C0            End Collection 
  74. */
  75.     #define myVendorID            0x05AC    // Apple
  76.     #define myProductID            0x0301    // USB mouse
  77.  
  78.     //    I have also used USB Prober to examine the Parsed Report Descriptor
  79.     // under the device's Configuration Descriptor. I chose a usage that i
  80.     // knew allowed values to be set. In this case RemainingCapacityLimit, 
  81.     // which is usage 41 on HID usage page 133. Find the listing for this
  82.     // item in the descriptor and scan upward to find report ID and report
  83.     // size that correspond to it. Also note that the item is described as
  84.     // Feature(Data, Variable, Absolute, Non-volatile). In the same fashion
  85.     // it may be possible for you to extract the necessary information to 
  86.     // interact with the HID item of your choice.
  87.  
  88.     #define targetUsage                0x30    // X
  89.     #define targetUsagePage            0x01    // Generic Desktop
  90.     #define targetCollection        0x01    // Pointer
  91.     #define targetCollectionPage    0x01    // Generic Desktop
  92.     #define targetReportID            0        // can be found from HID Library
  93.     #define targetReportBitSize        24        // cannot be found from HID Library.
  94.  
  95.  
  96.     // Forward declarations:
  97.     void InstallReportHandler();
  98.     void RemoveReportHandler();
  99.     void MyHIDReportHandler(void * inHIDReport, UInt32 inHIDReportLength, 
  100.                                     UInt32 inRefcon);
  101.         
  102.     // Data shared between functions
  103.     HIDDeviceDispatchTablePtr    mHIDDispatchTable;
  104.     USBDeviceDescriptorPtr        mUSBDeviceDesc;
  105.     HIDPreparsedDataRef            mParsedHIDRef;
  106.     HIDDeviceConnectionRef        mHIDDeviceConnectionRef;
  107.  
  108.     UInt32                         mReportID = targetReportID;
  109.     UInt32                         mCollection = 0;
  110.     UInt32                        mReportSize = (targetReportBitSize + 7)/8;    // Room for odd bits.
  111.  
  112.  
  113. int main(void)
  114. {
  115.     // Data shared between sections
  116.     CFragConnectionID    usbConnID;
  117.     CFragSymbolClass    symClass;
  118.     THz                    currentZone;
  119.     OSErr                 err;
  120.  
  121.  
  122. // ***** Step 1: Find Your Device:
  123.  
  124.     USBDeviceRef        usbDeviceRef = kNoDeviceRef;
  125.     Boolean                foundMyDevice = false;
  126.  
  127.     while (!foundMyDevice)
  128.     {
  129.         err = USBGetNextDeviceByClass (&usbDeviceRef, &usbConnID, kUSBHIDClass,
  130.                                         kUSBAnySubClass, kUSBAnyProtocol);
  131.         if (err) return err;
  132.  
  133.         // Need to be in the system zone when we search for the symbol.
  134.         currentZone = GetZone ();
  135.         SetZone (SystemZone ());
  136.         err = FindSymbol (usbConnID, "\pTheUSBDriverDescription", 
  137.                             (Ptr *)&mUSBDeviceDesc, &symClass);
  138.         SetZone (currentZone);
  139.  
  140.         if (mUSBDeviceDesc->vendor == myVendorID &&
  141.             mUSBDeviceDesc->product == myProductID)
  142.         {
  143.             foundMyDevice = true;
  144.         }
  145.         
  146.         // If the driver that matched our device was the HID class driver,
  147.         // it does not belong to a specific device, so it has vendor and
  148.         // product ids of 0, 0. In that case, we have to check farther.
  149.         
  150.         if (mUSBDeviceDesc->vendor == 0 &&
  151.             mUSBDeviceDesc->product == 0)
  152.         {
  153.             // Now we are going to get information from the device itself.
  154.             currentZone = GetZone();
  155.             SetZone(SystemZone());
  156.             err = FindSymbol(usbConnID, "\pTheHIDDeviceDispatchTable", 
  157.                                 (Ptr *)&mHIDDispatchTable, &symClass);
  158.             SetZone(currentZone);
  159.             if (err) continue;        // There may be other devices to check.
  160.  
  161.             UInt16 theVendorID = 0;
  162.             UInt32 size = sizeof(UInt16);
  163.             err = (*mHIDDispatchTable->pHIDGetDeviceInfo)(kHIDGetInfo_VendorID,
  164.                                                     &theVendorID, &size);
  165.             if (err) continue;
  166.  
  167.             UInt16 theProductID = 0;
  168.             size = sizeof(UInt16);
  169.             err = (*mHIDDispatchTable->pHIDGetDeviceInfo)(kHIDGetInfo_ProductID, 
  170.                                                     &theProductID, &size);
  171.             if (err) continue;
  172.  
  173.             if (theVendorID == myVendorID && theProductID == myProductID)
  174.             {
  175.                 foundMyDevice = true;
  176.             }
  177.         }
  178.     }
  179.     
  180.  
  181. // ***** Step 2: HID Library Setup:
  182.     
  183.     UInt8 *        mHIDReportDesc = nil;
  184.     UInt32        mHIDReportDescLength = 0;
  185.  
  186.     currentZone = GetZone();
  187.     SetZone(SystemZone());
  188.     err = FindSymbol(usbConnID, "\pTheHIDDeviceDispatchTable", 
  189.                         (Ptr *)&mHIDDispatchTable, &symClass);
  190.     SetZone(currentZone);
  191.     if (err) return err;
  192.  
  193.     // Find out what size buffer we need for HID report descriptor.
  194.     err = (*mHIDDispatchTable->pHIDGetHIDDescriptor)(kUSBReportDesc, 0, 
  195.                                         nil, &mHIDReportDescLength);
  196.     if (err) return err;
  197.  
  198.     mHIDReportDesc = (UInt8 *)NewPtrClear(mHIDReportDescLength);
  199.     if (mHIDReportDesc == nil) return MemError();
  200.  
  201.     err = (*mHIDDispatchTable->pHIDGetHIDDescriptor)(kUSBReportDesc, 0, 
  202.                                         mHIDReportDesc, &mHIDReportDescLength);
  203.     if (!err)
  204.         err = HIDOpenReportDescriptor(mHIDReportDesc, mHIDReportDescLength, 
  205.                                 &mParsedHIDRef, kHIDFlag_StrictErrorChecking);
  206.     // Some HID report descriptors may have minor errors that trip up our call
  207.     // when kHIDFlag_StrictErrorChecking is on. If may be possible to try this
  208.     // with 0 error checking and have the open succeed.
  209.     
  210.     // No longer need the raw descriptor.
  211.     DisposePtr((char *)mHIDReportDesc);
  212.  
  213.     if (err) goto finalcleanup;
  214.     
  215.     
  216. // ***** Step 3: Finding Report Info:
  217.     
  218.     // In order to get and set values, you need to know the report ID of the 
  219.     // report that will pass those values back and forth to your device. This
  220.     // information is usually known from the HID report descriptor and you 
  221.     // should be able to declare it as a constant. 
  222.     
  223.     // In those cases where you may want to find a particular type of usage in
  224.     // a number of different devices (each with differing HID reports), or just
  225.     // don't know the report ID, you can still find the report ID dynamically
  226.     // using the HID Library. You do need to know the usage, usage page, and
  227.     // report type (input, output, or feature) of the value you desire. This
  228.     // example is looking for a usage value, so it will use HIDValueCaps, but
  229.     // if you want button settings, you would use the corresponding HIDButtonCaps.
  230.  
  231.     // The code below is going to loop through the tables looking for the correct
  232.     // usage and usage page. What happens if you have several fields that use the
  233.     // same usage and usage page? To clearly specify an item, the HID Library
  234.     // passes a collection id with most of it's calls. Usually it is sufficient
  235.     // to pass a value of 0 which will match any collection. If a collection value
  236.     // is necessary to clearly specify which usage you need, the collection numbers
  237.     // are assigned by HID Library when it does it's pre-parsing, so you will have
  238.     // to first find the correct collection.
  239.  
  240.     HIDCaps            mMaxCaps;
  241.     UInt32            numCollections;
  242.     UInt32            numInputValues;
  243.     HIDCollectionNode *    cnPtr = nil;
  244.     HIDValueCaps *    vcPtr = nil;        // // For buttons, use HIDButtonCaps.
  245.     
  246.     
  247.     // Get statistics on how many of each type of HID item.
  248.     err = HIDGetCaps(mParsedHIDRef, &mMaxCaps);
  249.  
  250.     if (!err)
  251.     {    
  252.         // Find the collection first
  253.         numCollections = mMaxCaps.numberCollectionNodes;
  254.         cnPtr = 
  255.             (HIDCollectionNode *)NewPtrClear(sizeof(HIDCollectionNode) * numCollections);
  256.             
  257.         if (cnPtr != nil)
  258.         {
  259.             err = HIDGetCollectionNodes(cnPtr, &numCollections, mParsedHIDRef);
  260.  
  261.             if (!err)
  262.             {    // We have our information and can search for specific case.
  263.                 for (int i = 0; i < numCollections; i++)
  264.                 {
  265.                     // The advantage of climbing through the collection node
  266.                     // array is that if the HID report description is complex
  267.                     // and uses multiple collecitons that have the same usage,
  268.                     // we can find the collection that contains the target
  269.                     // collection or the collection that is the sibling of the
  270.                     // target collection. Then use that in the compare of a
  271.                     // further iteration, until we get to the unique collection
  272.                     // number we need.
  273.                     if (cnPtr[i].collectionUsage == targetCollection &&
  274.                         cnPtr[i].collectionUsagePage == targetCollectionPage)
  275.                     {
  276.                         mCollection = i;
  277.                         break;
  278.                     }
  279.                 }
  280.             }
  281.  
  282.             DisposePtr((char *)cnPtr);
  283.         }
  284.         // If we had an error looking for the collection, we could bail from here
  285.         // or just press on and hope that the first usage we find below works out.
  286.         if (err) goto finalcleanup;
  287.         
  288.         // There can be 6 arrays of HID report info: Both values and buttons can
  289.         // be grouped into kHIDInputReport, kHIDOutputReport, and kHIDFeatureReport.
  290.         // For simplicity, we will only check for an input value at this time.
  291.         numInputValues = mMaxCaps.numberInputValueCaps;
  292.         vcPtr = 
  293.             (HIDValueCaps *)NewPtrClear(sizeof(HIDValueCaps) * numInputValues);
  294.             
  295.         if (vcPtr != nil)
  296.         {
  297.             err = HIDGetValueCaps(kHIDInputReport, vcPtr, &numInputValues, 
  298.                                     mParsedHIDRef);
  299.             if (!err)
  300.             {    // We have our information and can search for specific case.
  301.                 for (int i = 0; i < numInputValues; i++)
  302.                 {
  303.                     // Before treating the usage field as a real usage, we
  304.                     // should have checked the isRange flag. However, we
  305.                     // won't match the targetUsage even if it is usageMin.
  306.                     if (vcPtr[i].u.notRange.usage == targetUsage &&
  307.                         vcPtr[i].usagePage == targetUsagePage &&
  308.                         // If the colleciton usage and usage page was 
  309.                         // unique, we skip the previous looping through
  310.                         // the collection nodes and just compare against
  311.                         // vcPtr[i].collectionUsage and vcPtr[i].collectionUsagePage
  312.                         // here.
  313.                         vcPtr[i].collection == mCollection)
  314.                     {
  315.                         // Notice if we didn't have to find a unique collection
  316.                         // first (and skipped it in the compare above), we could
  317.                         // still find the collection here to use in later function 
  318.                         // calls.
  319.                         // mCollection = vcPtr[i].collection;
  320.                         mReportID = vcPtr[i].reportID;
  321.                         break;
  322.                     }
  323.                 }
  324.             }
  325.  
  326.             DisposePtr((char *)vcPtr);
  327.         }
  328.     }
  329.  
  330.     if (err) goto finalcleanup;
  331.     
  332.  
  333. // ***** Step 4: Setup Report Handler:
  334.     
  335.     // The report handler will normally just let us know when a value has changed.
  336.     // But what if we want to Get an initial value?
  337.     // Normally we would do some sychronous read of the value we are interested in.
  338.     // However, USB is running at interrupt time and HID Library has gone to great
  339.     // lengths to shield us from that. So the way to cleanly get a value is to use
  340.     // the report handler. We will install the report handler and then request a
  341.     // report for the value we are interested in.
  342.  
  343.     InstallReportHandler();
  344.     
  345.  
  346. // ***** Step 5: Get Current Value:
  347.     
  348.     //    Installing the report handler will let us know when values change
  349.     // because that is when reports are issued. To get initial values, however,
  350.     // we may have to request them.
  351.     
  352.     // Prior to USB 1.4, the pHIDGetReport vector was nil, so there was no
  353.     // way to request a current value until after Mac OS 9.0.
  354.     if (mHIDDispatchTable->pHIDGetReport == nil) goto waitforinput;
  355.     
  356.     // We are not only asking for the value we are interested in, but also
  357.     // any value that shares that reportID.
  358.     err = (*mHIDDispatchTable->pHIDGetReport)(mHIDDeviceConnectionRef, 
  359.                 kHIDInputReport, mReportID, MyHIDReportHandler, 0);
  360.  
  361.  
  362. // ***** Step 6: Change Value:
  363.  
  364. //****************************************************************************
  365. // WARNING! We want an example of setting a value on a HID device. Well,
  366. // the only device we could count on being available for this sample code
  367. // was the Apple mouse. It has only input reports, because it is only sending
  368. // data to the Macintosh and doesn't expect any back. I will go ahead with
  369. // this example and construct a report using the kHIDInputReport type so you
  370. // can see how it is done. But clearly for setting a value, we should only be
  371. // sending values to kHIDOutputReports or those kHIDFeatureReports that are 
  372. // used to configure the device. For now, when we actually send this report
  373. // off to the device, it will reply with error -6912, stalling the pipe because
  374. // it doesn't understand. So we get to see a cascade of error messages output
  375. // to the USB Prober Expert Log. We'll recover and you can see the other input
  376. // reports get handled just fine.
  377. //****************************************************************************
  378.     
  379.     UInt32 newValue = 30;    // Random number for our example.
  380.     
  381.     // To use SetValue, we discovered that we needed more information than
  382.     // was easily available. To recitify this, future releases of the HID
  383.     // Library may include an expanded API that will handle some of the 
  384.     // setup we are doing here.
  385.  
  386.     // Prior to USB 1.4, the pHIDSetReport vector was nil, so there was no
  387.     // way to set a value until after Mac OS 9.0.
  388.     if (mHIDDispatchTable->pHIDSetReport == nil) goto waitforinput;
  389.  
  390.     // Create storage for the HID report we are going to send.
  391.     // The first thing we need to know is what size record to create. There
  392.     // is NO API in the HID Library that allows us to find this out. So for
  393.     // now, we must look at the HID Report Descriptor displayed by USB Prober.
  394.     // We will have to add up all the field sizes that share that report ID.
  395.  
  396.     // The size displayed is in bits, but the size we use in HID Library calls
  397.     // is in byte. In this example we have a total size of 24. To get bytes we
  398.     // use the formula: bytes = (bits + 7) / 8, which keeps us from rounding off
  399.     // those odd numbers of bits. Not only that, but when there are many possible 
  400.     // reportID's, we need an extra byte to hold the reportID we want. Since the
  401.     // mouse has only a single report it issues, there is no need for that extra
  402.     // byte in this example. 
  403.     
  404.     UInt8 * reportPtr = (UInt8 *)::NewPtrClear(mReportSize);
  405.     if (reportPtr == nil) goto waitforinput;
  406.     
  407.     // In the cases where our report must contain the report ID, HIDSetUsageValue
  408.     // doesn't set it for us. We must do so manually. (In this example, since the
  409.     // report ID is 0, it doesn't matter that we put it in the first byte where we
  410.     // don't need it.)
  411.     *reportPtr = mReportID;
  412.     
  413.     // The HID Library can now build the necessary HID report. Note here that
  414.     // a collection value of 0 is usually sufficient for the setup calls.
  415.     err = HIDSetUsageValue(kHIDInputReport, targetUsagePage, mCollection, 
  416.                 targetUsage,  newValue, mParsedHIDRef, reportPtr,  mReportSize);
  417.     if (err) goto setvaluecleanup;
  418.  
  419.     // Actually send the report.
  420.     err = (*mHIDDispatchTable->pHIDSetReport)(mHIDDeviceConnectionRef, 
  421.                 kHIDInputReport, mReportID, reportPtr, mReportSize);
  422.     if (err) goto setvaluecleanup;
  423.  
  424.     // Optional step. When a value is sent to a HID device, it usually does not
  425.     // reply with a new report showing the changed value. So if you are using 
  426.     // your report handler to keep track of current values, you may want to 
  427.     // request a new report to verify that our set report actually worked.
  428.     err = (*mHIDDispatchTable->pHIDGetReport)(mHIDDeviceConnectionRef, 
  429.                 kHIDInputReport, mReportID, MyHIDReportHandler, 0);
  430.     
  431. setvaluecleanup:
  432.     // HID Library makes it's own copy of our data, so we can free it now.
  433.     if (reportPtr != nil) DisposePtr((char *)reportPtr);
  434.  
  435.  
  436. // ***** Step 7: Display Output:
  437.     
  438.     // Allow time for reports to come in and be handled before quiting.
  439.     // The report handler will be given time to add output to USB Prober's
  440.     // Expert Log window.
  441. waitforinput:
  442.     printf("View report handling in USB Prober's Expert Log window.\n");
  443.     printf("Type 'q' to quit handling reports.\n");
  444.     fflush(nil);
  445.     int stopChar = 0;
  446.     while (stopChar != 'q' && stopChar != 'Q') stopChar = getchar();
  447.  
  448.  
  449. // ***** Step 8: Cleanup:
  450.     
  451. finalcleanup:                            
  452.     RemoveReportHandler();
  453.  
  454.     printf("Removed report handler.\n");
  455.     fflush(nil);
  456.  
  457.     if (mParsedHIDRef != nil)
  458.     {
  459.         HIDCloseReportDescriptor(mParsedHIDRef);
  460.         mParsedHIDRef = nil;
  461.     }
  462.  
  463.     return 0;
  464. }
  465.  
  466.  
  467. //        • ReportHandler
  468. //
  469. //    The key to how the report handler works is that when it gets called to 
  470. // process a report, it must pass the report through one of the HID Library
  471. // functions to extract the desired value. These decoder functions are called
  472. // HIDGetxxx (not HIDGetxxxCaps): HIDGetUsageValue, HIDGetScaledUsageValue,
  473. // HIDGetUsageValueArray, HIDGetButtons, and HIDGetButtonsOnPage. 
  474. //    Reports are compressed data that may contain multiple values within. They
  475. // also may or may not have reportID as the first byte of data. A brute force
  476. // way to handle the confusion is to just set up a series of decode calls for
  477. // each value you are interested in and apply the incomming report to each of
  478. // them, accepting only those that return with noErr.
  479.  
  480. void MyHIDReportHandler(void * inHIDReport, UInt32 inHIDReportLength, 
  481.                                     UInt32 inRefcon)
  482. {
  483.     // In the HID Browser, i have multiple devices open, each with it's own
  484.     // set of values. There i pass the pointer to the device's variables
  485.     // as the refcon.
  486.     #pragma unused(inRefcon)
  487.  
  488.     // A special note for using HIDGetButtonsOnPage: This call is going to
  489.     // return an array of HIDUsage's that corresponds to each button that is
  490.     // on. In the advent of there being no buttons on, rather than just
  491.     // returning a 0 length array, it comes back with kHIDUsageNotFoundErr.
  492.  
  493.     // We're making it easy. We're only looking for one specific value.
  494.     SInt32 reportValue;
  495.     OSErr err;
  496.  
  497.     err = HIDGetUsageValue(kHIDInputReport, targetUsagePage, mCollection, 
  498.                     targetUsage, &reportValue, mParsedHIDRef, inHIDReport, 
  499.                     inHIDReportLength);
  500.     if (err) return;
  501.  
  502.     // My first attempt at a cheap thing to do with our output, was to use printf.
  503.     // After crashing due to interference between printf's in the main code thread
  504.     // and this handler, i hit upon the much better expediant of sending our output
  505.     // to USB Prober's Expert Log window. Plus, this is a great example of an
  506.     // invaluable debugging tool for USB.
  507.     USBExpertStatusLevel(kUSBStatusLevelGeneral, 0, 
  508.                     "\pMyHIDReportHandler Value: ", (UInt32)reportValue);
  509.     
  510.     // We're done handling the report, but now that this example gets reports from
  511.     // the mouse, they are no longer going on to the system to move the cursor. The
  512.     // polite thing to do is to forward the reports on to whoever else may need them.
  513.     if (mHIDDispatchTable->pHIDCallPreviousReportHandler != nil &&
  514.         mHIDDeviceConnectionRef != 0)
  515.     {
  516.         // Call previous handler.
  517.         err = (*mHIDDispatchTable->pHIDCallPreviousReportHandler)
  518.                 (mHIDDeviceConnectionRef, inHIDReport, inHIDReportLength);
  519.     }
  520. }
  521.  
  522.  
  523. //        • InstallReportHandler
  524. //
  525. //    Since InstallReportHandler can be called from various locations,
  526. // it does all it's own setup validation before doing the actual
  527. // install. If it fails, it leaves the state such that RemoveReportHandler
  528. // can also be called and perform only necessary cleanup.
  529.  
  530. void InstallReportHandler()
  531. {
  532.     HIDDeviceConnectionRef tempDeviceConnectionRef;
  533.     OSErr err;
  534.  
  535.     // Checking to see if device open already.
  536.     if (mHIDDeviceConnectionRef != 0) return;
  537.  
  538.     // Are we setup to open?
  539.     if (mHIDDispatchTable == nil) return;
  540.  
  541.     // Open the device.
  542.     err = (*mHIDDispatchTable->pHIDOpenDevice)
  543.                     (&tempDeviceConnectionRef, kHIDPerm_ReadWriteShared, 0);
  544.  
  545.     if (err) return;
  546.  
  547.     // The device is open, so let's install our handler.
  548.     err = (*mHIDDispatchTable->pHIDInstallReportHandler)
  549.                     (tempDeviceConnectionRef, 0, MyHIDReportHandler, 0);
  550.     
  551.     // Don't leave device open if we got an error.
  552.     if (err)
  553.     {
  554.         (*mHIDDispatchTable->pHIDCloseDevice)(tempDeviceConnectionRef);
  555.         return;
  556.     }
  557.     
  558.     // Signal successful opening.
  559.     mHIDDeviceConnectionRef = tempDeviceConnectionRef;
  560. }
  561.  
  562.  
  563. //        • RemoveReportHandler
  564. //
  565. //    Since RemoveReportHandler can be called from various locations,
  566. // it does all it's own setup validation before doing the actual remove.
  567.  
  568. void RemoveReportHandler()
  569. {
  570.     OSErr err;
  571.     
  572.     // If no indication that we successfully installed, don't remove.
  573.     if (mHIDDeviceConnectionRef == 0) return;
  574.     
  575.     // Remove the handler.
  576.     err = (*mHIDDispatchTable->pHIDRemoveReportHandler)(mHIDDeviceConnectionRef);
  577.  
  578.     // Removing the report handler also restores the previous handler, if any.
  579.     // In event of an error so that is not done, it may still be better to fall
  580.     // through and try to close the device anyway?
  581.     if (err) return;
  582.  
  583.     // Release the device.
  584.     err = (*mHIDDispatchTable->pHIDCloseDevice)(mHIDDeviceConnectionRef);
  585.  
  586.     if (err) return;
  587.     
  588.     // Signal successful closing.
  589.     mHIDDeviceConnectionRef = 0;
  590. }
  591.  
  592.